package com.bckj.fastboot.iot.parse.core;

import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.NumberUtil;
import com.bckj.fastboot.core.extra.cache.CacheOps;
import com.bckj.fastboot.iot.api.service.IotProtocolRemoteService;
import com.bckj.fastboot.iot.common.constant.IotMQConstants;
import com.bckj.fastboot.iot.common.dto.msg.IotDeviceMessage;
import com.bckj.fastboot.iot.common.dto.request.IotAlarmDataRequest;
import com.bckj.fastboot.iot.common.dto.request.IotBasicDataRequest;
import com.bckj.fastboot.iot.common.dto.request.IotStatusDataRequest;
import com.bckj.fastboot.iot.common.entity.IotProtocolItem;
import com.bckj.fastboot.iot.common.mq.producer.IotDeviceMessageProducer;
import com.bckj.fastboot.iot.common.constant.IotParseCacheConstants;
import com.bckj.fastboot.iot.parse.service.IotAlarmDataService;
import com.bckj.fastboot.iot.parse.service.IotBasicDataService;
import com.bckj.fastboot.iot.parse.service.IotStatusDataService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * AbstractProtocolParser
 *
 * @author wfc
 **/
@Slf4j
public abstract class AbstractProtocolParser {

    @Resource
    private IotProtocolRemoteService iotProtocolRemoteService;
    @Resource
    private IotBasicDataService iotBasicDataService;
    @Resource
    private IotAlarmDataService iotAlarmDataService;
    @Resource
    private IotStatusDataService iotStatusDataService;
    @Resource
    private IotAlarmTrigger iotAlarmTrigger;
    @Resource
    private IotDeviceMessageProducer iotDeviceMessageProducer;
    @Resource
    private CacheOps cacheOps;

    public abstract void parse(Object protocolData, String devCode, Object... extra);


    protected List<IotProtocolItem> getProtocolItemList(String protocolCode, String cmd) {
        String cacheKey = protocolCode + cmd;
        var cache = cacheOps.localCache(IotParseCacheConstants.IOT_PROTOCOL_CACHE_NAME);
        return cacheOps.runCaching(cache, () -> iotProtocolRemoteService.findItems(protocolCode, cmd), cacheKey);
    }

    protected Map<String, Object> parseData(byte[] data, List<IotProtocolItem> protocolItemList) {
        Map<String, Object> result =new HashMap<>();
        for (IotProtocolItem item : protocolItemList) {
            int startIndex = item.getStartIndex();
            int len = item.getLen();
            int endIndex = startIndex + len;
            if (endIndex > data.length) {
                log.warn("解析失败【{}】，数据长度不足", item.getName());
                continue;
            }
            byte[] bytes = new byte[len];
            System.arraycopy(data, startIndex, bytes, 0, len);
            result.put(item.getCode(),
                    parse(bytes, item.getParseRule(), item.getScale()));
        }
        return result;
    }

    private Object parse(byte[] bytes, String parseRule, Integer scale) {
        if (isAllFF(bytes)) {
            return null;
        }
        return switch (parseRule) {
            case "int" -> {
                int i = ByteUtil.bytesToInt(bytes);
                yield scale != null ? NumberUtil.div((Number) i, scale, 2).doubleValue() : i;
            }
            case "long" -> {
                long l = ByteUtil.bytesToLong(bytes);
                yield scale != null ? NumberUtil.div((Number) l, scale, 2).doubleValue() : l;
            }
            case "float" -> {
                float f = ByteUtil.bytesToFloat(bytes);
                yield scale != null ? NumberUtil.div((Number) f, scale, 2).doubleValue() : f;
            }
            case "double" -> {
                double d = ByteUtil.bytesToDouble(bytes);
                yield scale != null ? NumberUtil.div((Number) d, scale, 2).doubleValue() : d;
            }
            case "short", "binary", "alarm" -> ByteUtil.bytesToShort(bytes);
            case "datetime" -> "%02d-%02d-%02d %02d:%02d:%02d".formatted(2000 + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes.length > 5 ? bytes[5] : 0);
            case "time" -> bytes.length > 2 ? "%02d:%02d:%02d".formatted(bytes[0], bytes[1], bytes[2]) : "%02d:%02d".formatted(bytes[0], bytes[1]);
            default -> null;
        };
    }

    private boolean isAllFF(byte[] data) {
        for(byte oneByte:data){
            if(oneByte!=(byte)0xFF){
                return false;
            }
        }
        return true;
    }

    protected void handleHeartbeat(ProtocolParseContext context) {
        Map<String, Object> dataMap = context.getDataMap();
        String updateTime = MapUtils.getString(dataMap, "updateTime");
        // 处理心跳
        var message = new IotDeviceMessage();
        message.setDevCode(context.getDevCode());
        message.setTag(IotMQConstants.IOT_DEVICE_HEARTBEAT_TAG);
        message.setHeartbeatTime(updateTime);
        iotDeviceMessageProducer.send(message);
    }

    protected IotBasicDataRequest buildBasicData(ProtocolParseContext context) {
        IotBasicDataRequest request = new IotBasicDataRequest();
        request.setDevCode(context.getDevCode());
        request.setCmd(context.getCmd());
        request.setDataMap(context.getDataMap());
        return request;
    }

    protected void handleBasicData(IotBasicDataRequest request) {
        iotBasicDataService.handle(request);
    }

    protected IotAlarmDataRequest buildAlarmData(ProtocolParseContext context) {
        IotAlarmDataRequest request = new IotAlarmDataRequest();
        request.setDevCode(context.getDevCode());
        request.setCmd(context.getCmd());
        request.setDataMap(context.getDataMap());
        return request;
    }

    protected void handleAlarmData(IotAlarmDataRequest request) {
        iotAlarmDataService.handle(request);
    }

    protected IotStatusDataRequest buildStatusData(ProtocolParseContext context) {
        IotStatusDataRequest request = new IotStatusDataRequest();
        request.setDevCode(context.getDevCode());
        request.setCmd(context.getCmd());
        request.setDataMap(context.getDataMap());
        return request;
    }

    protected void handleStatusData(IotStatusDataRequest request) {
        iotStatusDataService.handle(request);
    }

    protected void triggerAlarm(ProtocolParseContext context) {
        iotAlarmTrigger.trigger(context);
    }
}
